﻿///////////////////////////////////////////////////////////////
//                                                           //
// Code from Arcanea Project.                                //
//                                                           //
// Please include this license part to every reproduction    //
//   of code.                                                //
//                                                           //
// This code is under Ms-RL license                          //
//                                                           //
// Please read futher information about the license,         //
//   the project (news and updates) and the                  //
//   contributors on the address :                           //
//     http://www.codeplex.com/Arcanea/                      //
//                                                           //
///////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using Arcanea.NetworkEngine.Abstract.Engines;
using System.Net.Sockets;
using Arcanea.Mediator.Exceptions;
using System.Net;

namespace Arcanea.NetworkEngine.Concrete.Engines.Protocole
{
    /// <summary>
    /// Protocole engine de connection TCP serveur
    /// </summary>
    public class TCPServerProtocoleEngine : IProtocoleEngine
    {
        private List<Socket> acceptedSockets = new List<Socket>();

        public TCPServerProtocoleEngine(int port)
        {
            netInformations.PortPrincipal = port;
        }

        #region Functions non utilisées

        public override bool Connect()
        {
            throw new BadFunctionUseException();
        }

        public override bool Send(byte[] array)
        {
            throw new BadFunctionUseException();
        }

        public override byte[] Receive(int taille)
        {
            throw new BadFunctionUseException();
        }

        #endregion

        #region Functions utilisées

        public override bool Bind()
        {
            netInformations.Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint hostep = new IPEndPoint(IPAddress.Any, netInformations.PortPrincipal);
            netInformations.Socket.Bind(hostep);
            netInformations.Socket.Listen(80);
            netInformations.Socket.BeginAccept(Accepted, null);
            return true;
        }

        public override NetInformationContainer AcceptedConnection()
        {
            Socket s = null;
            lock (acceptedSockets)
            {
                if (acceptedSockets.Count != 0)
                {
                    s = acceptedSockets[0];
                    acceptedSockets.RemoveAt(0);
                }
            }

            if (s == null) return null;

            NetInformationContainer result = netInformations.Clone();
            result.Socket = s;
            return result;
        }

        #endregion

        #region Private functions

        private void Accepted(IAsyncResult result)
        {
            try
            {
                Socket s = null;

                lock (netInformations.Socket)
                {
                    s = netInformations.Socket.EndAccept(result);
                }

                lock (acceptedSockets)
                {
                    acceptedSockets.Add(s);
                }
                logger.Debug("A client has been accepted");
                netInformations.Socket.BeginAccept(Accepted, null);
            }
            catch (ObjectDisposedException)
            {
                //Levé lors de la fermeture du serveur, donc trés ponctuellement.
            }
            catch (Exception e)
            {
                //Reste des erreurs
                logger.Error("Error on accepting client : " + e.Message);
            }
        }

        #endregion
    }
}
